/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package victoralbertos.io.ohos.net;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import io.rx_cache.DynamicKey;
import io.rx_cache.EvictDynamicKey;
import io.rx_cache.EvictProvider;
import io.rx_cache.Reply;
import io.rx_cache.entities.Repo;
import io.rx_cache.entities.User;
import io.rx_cache.internal.RxCache;

import java.io.File;
import java.io.IOException;
import java.util.List;

import lib.jolyglot.GsonSpeaker;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
import rx.functions.Func1;
import victoralbertos.io.ohos.cache.CacheProviders;

/**
 * 接口调用
 *
 * @author name
 * @since 2021-05-30
 */
public class Repository {
    /**
     * 每页数量
     */
    public static final int USERS_PER_PAGE = 25;

    private final CacheProviders cacheProviders;
    private final RestApi restApi;

    /** 构造方法
     * @param cacheDir
     */
    public Repository(File cacheDir) {
        cacheProviders = new RxCache.Builder()
                .persistence(cacheDir, new GsonSpeaker())
                .using(CacheProviders.class);

        restApi = new Retrofit.Builder()
                .baseUrl(RestApi.URL_BASE)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build().create(RestApi.class);
    }

    /** init
     * @param cacheDir
     * @return Repository
     */
    public static Repository init(File cacheDir) {
        return new Repository(cacheDir);
    }

    /** getUsers
     * @param idLastUserQueried
     * @param isUpdate
     * @return Observable
     */
    public Observable<Reply<List<User>>> getUsers(int idLastUserQueried, final boolean isUpdate) {
        Observable<List<User>> users = restApi.getUsers(idLastUserQueried, USERS_PER_PAGE);
        return cacheProviders.getUsers(users, new DynamicKey(idLastUserQueried), new EvictDynamicKey(false));
    }

    /** getLockUsers
     * @param idLastUserQueried
     * @param isUpdate
     * @return Observable
     */
    public Observable<Reply<List<User>>> getLockUsers(int idLastUserQueried, final boolean isUpdate) {
        Observable<List<User>> users = restApi.getUsers(idLastUserQueried, USERS_PER_PAGE);
        return cacheProviders.getLockUsers(users, new DynamicKey(idLastUserQueried), new EvictDynamicKey(false));
    }

    /** getRepos
     * @param userName
     * @param isUpdate
     * @return Observable
     */
    public Observable<Reply<List<Repo>>> getRepos(final String userName, final boolean isUpdate) {
        return cacheProviders.getRepos(restApi.getRepos(userName),
                new DynamicKey(userName), new EvictDynamicKey(isUpdate));
    }

    /** 登录
     * @param userName
     * @return Observable
     */
    public Observable<Reply<User>> loginUser(final String userName) {
        return restApi.getUser(userName).map(new Func1<Response<User>, Observable<Reply<User>>>() {
            @Override
            public Observable<Reply<User>> call(Response<User> userResponse) {
                if (!userResponse.isSuccessful()) {
                    try {
                        ResponseError responseError = new Gson().fromJson(userResponse.errorBody().string(),
                                ResponseError.class);
                        throw new RuntimeException(responseError.getMessage());
                    } catch (JsonParseException | IOException exception) {
                        throw new RuntimeException(exception.getMessage());
                    }
                }
                return cacheProviders.getCurrentUser(Observable.just(userResponse.body()), new EvictProvider(true));
            }
        }).flatMap(new Func1<Observable<Reply<User>>, Observable<Reply<User>>>() {
            @Override
            public Observable<Reply<User>> call(Observable<Reply<User>> replyObservable) {
                return replyObservable;
            }
        }).map(new Func1<Reply<User>, Reply<User>>() {
            @Override
            public Reply<User> call(Reply<User> userReply) {
                return userReply;
            }
        });
    }

    /** 登出
     * @return Observable
     */
    public Observable<String> logoutUser() {
        return cacheProviders.getCurrentUser(Observable.<User>just(null), new EvictProvider(true))
                .map(new Func1<Reply<User>, String>() {
                    @Override
                    public String call(Reply<User> user) {
                        return "Logout";
                    }
                })
                .onErrorReturn(new Func1<Throwable, String>() {
                    @Override
                    public String call(Throwable throwable) {
                        return "Logout";
                    }
                });
    }

    /** 获取登录用户
     * @param isInvalidate
     * @return Observable
     */
    public Observable<Reply<User>> getLoggedUser(boolean isInvalidate) {
        Observable<Reply<User>> cachedUser = cacheProviders.getCurrentUser(Observable.<User>just(null),
                new EvictProvider(false));

        Observable<Reply<User>> freshUser = cachedUser.flatMap(new Func1<Reply<User>, Observable<Reply<User>>>() {
            @Override
            public Observable<Reply<User>> call(Reply<User> userReply) {
                return loginUser(userReply.getData().getLogin());
            }
        });

        if (isInvalidate) {
            return freshUser;
        } else {
            return cachedUser;
        }
    }

    /**
     * 接口调用错误回调类
     *
     * @author name
     * @since 2021-05-30
     */
    private static class ResponseError {
        private final String message;

        ResponseError(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }
}
